package com.newcapec.shiro.shiro;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.xkp.ColorPrint;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
@Slf4j
public class ShiroConfig {


    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        ColorPrint.redPrintln("Shiro过滤器执行...shiroFilter");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //必须设置SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //需要设置登录的接口: 没有登录时给用户的提示（如果是在非前后端分离的项目中，配置跳转登录页面）
        shiroFilterFactoryBean.setLoginUrl("/pub/needLogin");
        //需要设置登录成功之后的跳转（如果是在非前后端分离的项目中，配置跳转首页页面）
        shiroFilterFactoryBean.setSuccessUrl("/");




        //拦截路径配置：此配置放在Map集合中，并且需要有顺序要求LinkedHashMap<key(过滤器的路径), value(过滤器别名)>
        Map<String, String> filterChainMap = new LinkedHashMap<>();
        //配置登出过滤器
        filterChainMap.put("/logout", "logout");
        //匿名可访问
        filterChainMap.put("/login", "anon");
        filterChainMap.put("/pub/**", "anon");
        //角色过滤器
//        filterChainMap.put("/admin/**", "roles[admin,root]");
        //权限过滤器
//        filterChainMap.put("/pers/**", "perms[emp:add,dept:remove]");
        //登录过滤器：必须完成身份认证才能访问的路径
        //配置所有其他路径的规则过滤器一定是放置在整个过滤器过滤链的最后
        filterChainMap.put("/**", "authc");

        //在shiroFilterFactoryBean中设置过滤器路径集合
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);


        return shiroFilterFactoryBean;
    }


    @Bean
    public SecurityManager createSecurityManager(){

        log.info("Shiro过滤器执行...SecurityManager");
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();

        defaultWebSecurityManager.setSessionManager(sessionManager());
        System.out.println(121323);
        // 2.创建Realm对象: SimpleAccountRealm管理账户的Realm
       // SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
        // 设置基础用户信息
//        simpleAccountRealm.addAccount("admin", "123");
//        simpleAccountRealm.addAccount("tom", "111");
        defaultWebSecurityManager.setRealm(createCustomRealm());
        return  defaultWebSecurityManager;
    }

    @Bean
    public CustomRealm   createCustomRealm(){
        CustomRealm customRealm = new CustomRealm();

        //在自定义Realm中设置密码加密匹配器
        customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return customRealm;
    }

    /**
     * 配置密码加密规则匹配器
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //设置散列算法
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //设置散列次数
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }




    /**
     * Shiro中的会话是由DefaultWebSessionManager进行管理
     * DefaultWebSessionManager默认是利用Cookie传输SessionId会话标识
     *
     * 在前后端分离的项目中，前端有可能不支持Cookie
     *
     * 配置SessionManager
     */
    @Bean
    public SessionManager sessionManager(){
        //CustomSessionManager customSessionManager = new CustomSessionManager();

        DefaultWebSessionManager customSessionManager=new DefaultWebSessionManager();
        //设置Session的有效时间, 单位为毫秒
        customSessionManager.setGlobalSessionTimeout(2*60*60*1000);

        //配置Session持久化
        customSessionManager.setSessionDAO(redisSessionDAO());

        return customSessionManager;
    }

    /**
     * Session持久化: 配置SessionDao
     * 缓存持久化：ehcache, redis
     * 多服务实例中共享Session，服务重启用户无感知
     */
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(getRedisManager());

        //设置sessionId生成器
        //redisSessionDAO.setSessionIdGenerator(new CustomSessionIdGenerator());

        //设置session存储到redis中之后的失效时间, 单位是秒
        redisSessionDAO.setExpire(2*60*60);
        return redisSessionDAO;
    }

    /**
     * 缓存管理器CacheManager
     * 配置缓存管理器
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        //设置RedisManager
        redisCacheManager.setRedisManager(getRedisManager());
        //设置缓存过期时间, 单位是秒
        redisCacheManager.setExpire(5*60);
        return redisCacheManager;
    }

    /**
     * 配置RedisManager
     */
    public RedisManager getRedisManager(){
        RedisManager redisManager = new RedisManager();
        redisManager.setHost("192.168.91.247");
        redisManager.setPort(6379);
        redisManager.setDatabase(0);
        return redisManager;
    }






    /**
     * 开启注解鉴权
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(createSecurityManager());
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * LifecycleBeanPostProcessor管理一些Bean的生命周期的
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 用于扫描所有通知器Advisor的对象
     */
    @Bean
//    @DependsOn("lifecycleBeanPostProcessor")
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }
}
